﻿package kit4j.ui.cs.full;

import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;

/**
 * 组件背景图像绘制及设置辅助类型。
 * 
 * @author MiniKnife
 * 
 */
public class FullComponentBackgroundPaintHelper {

	/**
	 * 为组件绘制背景图像。
	 * 
	 * @param component
	 *            要绘制背景图像的组件
	 * @param img
	 *            要绘制的背景图像
	 * @param bgc
	 *            背景图像布局属性
	 * @param g
	 *            绘制图像的图形上下文
	 * 
	 * @throws NullPointerException
	 *             如果component或者img参数为null，抛出此异常。
	 */
	public static void paintBackground(Component component, Image img, BackgroundConstraints bgc, Graphics g)
			throws NullPointerException {
		if (component == null) {
			throw new NullPointerException("component shouldn't be null.");
		}

		if (img == null) {
			throw new NullPointerException("image shouldn't be null.");
		}

		if (bgc == null) {
			bgc = new BackgroundConstraints();
		}

		BackgroundConstraintsPropertyAnalyst bcpa = BackgroundConstraintsPropertyAnalyst.getInstance(bgc, component,
				img);

		int distX1 = bcpa.getDistX1(), distY1 = bcpa.getDistY1(), distX2 = bcpa.getDistX2(), distY2 = bcpa.getDistY2(), srcX1 = bcpa
				.getSourceX1(), srcY1 = bcpa.getSourceY1(), srcX2 = bcpa.getSourceX2(), srcY2 = bcpa.getSourceY2();

		// 图像显示的宽高属性
		int drawWidth = distX2 - distX1, drawHeight = distY2 - distY1;

		// 根据图像的重复模式进行绘制
		switch (bgc.repeat) {
		case BackgroundConstraints.NONE:
			g.drawImage(img, distX1, distY1, distX2, distY2, srcX1, srcY1, srcX2, srcY2, component.getBackground(),
					component);
			break;
		case BackgroundConstraints.HORIZONTAL:
			for (int x = distX1; x <= component.getWidth(); x += drawWidth) {
				g.drawImage(img, x, distY1, x + drawWidth, distY2, srcX1, srcY1, srcX2, srcY2,
						component.getBackground(), component);
			}
			break;
		case BackgroundConstraints.VERTICAL:
			for (int y = distY1; y <= component.getHeight(); y += drawHeight) {
				g.drawImage(img, distX1, y, distX2, y + drawHeight, srcX1, srcY1, srcX2, srcY2,
						component.getBackground(), component);
			}
			break;
		case BackgroundConstraints.BOTH:
			for (int x = distX1; x <= component.getWidth(); x += drawWidth) {
				for (int y = distY1; y <= component.getHeight(); y += drawHeight) {
					g.drawImage(img, x, y, x + drawWidth, y + drawHeight, srcX1, srcY1, srcX2, srcY2,
							component.getBackground(), component);
				}
			}
			break;
		}
	}

	/**
	 * 为组件的所有子组件设置背景图像。 子组件的中设置的背景图像内容为背景图像相对组件的位置与子组件相对组件的位置的重合部分。 特别的，如果某个子组件没有实现FullComponent接口，则不为该子组件设置背景图。
	 * 
	 * @param container
	 *            要设置背景图像的所有子组件的父组件
	 * @param img
	 *            要绘制的背景图像
	 * @param bgc
	 *            背景图像布局属性
	 * 
	 * @throws NullPointerException
	 *             如果container或者img参数为null，抛出此异常。
	 */
	public static void setComponentsBackground(Container container, Image img, BackgroundConstraints bgc)
			throws NullPointerException {
		if (container == null) {
			throw new NullPointerException("container shouldn't be null.");
		}

		if (img == null) {
			throw new NullPointerException("image shouldn't be null.");
		}

		if (bgc == null) {
			bgc = new BackgroundConstraints();
		}

		// 设置自组件的背景图
		for (Component com : container.getComponents()) {
			setComponentExtendedBackground(com, img, bgc);
		}
	}

	/**
	 * 为父组件的某个子组件设置背景图像。 子组件的中设置的背景图像内容为背景图像相对组件的位置与子组件相对组件的位置的重合部分。
	 * 
	 * @param component
	 *            要设置背景图像的组件，注意：该component必须实现FullComponent接口
	 * @param img
	 *            父组件的背景图像
	 * @param bgc
	 *            父组件的背景图像布局属性
	 * 
	 * @throws NullPointerException
	 *             如果component、img参数或者component.getContainer()返回值为null，抛出此异常。
	 * @throws IllegalArgumentException
	 *             如果component参数没有实现FullComponent接口，抛出此异常。
	 */
	public static void setComponentExtendedBackground(Component component, Image img, BackgroundConstraints bgc)
			throws NullPointerException, IllegalArgumentException {
		if (component == null) {
			throw new NullPointerException("component shouldn't be null.");
		}

		if (!(component instanceof FullComponent)) {
			throw new IllegalArgumentException("component should be a FullComponent.");
		}

		if (img == null) {
			throw new NullPointerException("image shouldn't be null.");
		}

		if (bgc == null) {
			bgc = new BackgroundConstraints();
		}

		Container container = component.getParent();

		if (container == null) {
			throw new NullPointerException("parent shouldn't be null.");
		}

		BackgroundConstraintsPropertyAnalyst bcpa = BackgroundConstraintsPropertyAnalyst.getInstance(bgc, container,
				img);

		int distWidth, distHeight;

		if (bgc.position == BackgroundConstraints.ABSOLUTE) {
			distWidth = bgc.width;
			distHeight = bgc.height;
		} else {
			distWidth = bcpa.getDistX2() - bcpa.getDistX1();
			distHeight = bcpa.getDistY2() - bcpa.getDistY1();
		}

		// 构建组件的背景图布局属性
		BackgroundConstraints cBgc = new BackgroundConstraints(BackgroundConstraints.ABSOLUTE, bcpa.getDistStartX()
				- component.getX(), bcpa.getDistStartY() - component.getY(), distWidth, distHeight, bgc.fill,
				bgc.repeat);

		// 设置组件的背景图与布局属性
		((FullComponent) component).setBackgroundImage(img);
		((FullComponent) component).setBackgroundConstraints(cBgc);
	}

	public static abstract class BackgroundConstraintsPropertyAnalyst {

		/**
		 * 返回背景图像在目标显示区域显示的左上角X轴坐标。 注意：如果BackgroundConstraints对象的repeat属性值为BackgroundConstraints
		 * .BOTH或者BackgroundConstraints.HORIZONTAL，则该方法只会返回第一次重复时显示的坐标。 如果需要获取重复完成后的坐标，应该调用getDistStartX()方法。
		 * 
		 * @return 显示图像的左上角X轴坐标
		 */
		abstract public int getDistX1();

		/**
		 * 返回背景图像在目标显示区域显示的左上角Y轴坐标。 注意：如果BackgroundConstraints对象的repeat属性值为BackgroundConstraints
		 * .BOTH或者BackgroundConstraints.HORIZONTAL，则该方法只会返回第一次重复时显示的坐标。 如果需要获取重复完成后的坐标，应该调用getDistStartY()方法。
		 * 
		 * @return 显示图像的左上角Y轴坐标
		 */
		abstract public int getDistY1();

		/**
		 * 返回背景图像在目标显示区域显示的左上角X轴坐标。 注意：如果BackgroundConstraints对象的repeat属性值为BackgroundConstraints
		 * .BOTH或者BackgroundConstraints.HORIZONTAL，则该方法只会返回第一次重复时显示的坐标。 如果需要获取重复完成后的坐标，应该调用getDistEndX()方法。
		 * 
		 * @return 显示图像的左上角X轴坐标
		 */
		abstract public int getDistX2();

		/**
		 * 返回背景图像在目标显示区域显示的左上角Y轴坐标。 注意：如果BackgroundConstraints对象的repeat属性值为BackgroundConstraints
		 * .BOTH或者BackgroundConstraints.HORIZONTAL，则该方法只会返回第一次重复时显示的坐标。 如果需要获取重复完成后的坐标，应该调用getDistEndY()方法。
		 * 
		 * @return 显示图像的左上角Y轴坐标
		 */
		abstract public int getDistY2();

		/**
		 * 返回背景图像需要被显示的左上角的X轴坐标。
		 * 
		 * @return 要被显示的左上角的X轴坐标
		 */
		abstract public int getSourceX1();

		/**
		 * 返回背景图像需要被显示的左上角的Y轴坐标。
		 * 
		 * @return 要被显示的左上角的Y轴坐标
		 */
		abstract public int getSourceY1();

		/**
		 * 返回背景图像需要被显示的右下角的X轴坐标。
		 * 
		 * @return 要被显示的右下角的X轴坐标
		 */
		abstract public int getSourceX2();

		/**
		 * 返回背景图像需要被显示的右下角的Y轴坐标。
		 * 
		 * @return 要被显示的右下角的Y轴坐标
		 */
		abstract public int getSourceY2();

		/**
		 * 返回背景图像在显示区域所占区域的左上角的X轴坐标。
		 * 
		 * @return 所占区域的左上角的X轴坐标
		 */
		abstract public int getDistStartX();

		/**
		 * 返回背景图像在显示区域所占区域的左上角的Y轴坐标。
		 * 
		 * @return 所占区域的左上角的Y轴坐标
		 */
		abstract public int getDistStartY();

		/**
		 * 返回背景图像在显示区域所占区域的右下角的X轴坐标。
		 * 
		 * @return 所占区域的右下角的X轴坐标
		 */
		abstract public int getDistEndX();

		/**
		 * 返回背景图像在显示区域所占区域的右下角的Y轴坐标。
		 * 
		 * @return 所占区域的右下角的Y轴坐标
		 */
		abstract public int getDistEndY();

		public static BackgroundConstraintsPropertyAnalyst getInstance(final BackgroundConstraints bgc,
				final Component component, final Image img) {
			return new BackgroundConstraintsPropertyAnalyst() {

				private final int componentWidth = component.getWidth();

				private final int componentHeight = component.getHeight();

				private final int imgWidth = img.getWidth(null);

				private final int imgHeight = img.getHeight(null);

				@Override
				public int getDistX1() {
					if (bgc.position == BackgroundConstraints.ABSOLUTE) {
						return bgc.x;
					}

					if (bgc.repeat == BackgroundConstraints.HORIZONTAL || bgc.repeat == BackgroundConstraints.BOTH) {
						return 0;
					}

					switch (bgc.fill) {
					case BackgroundConstraints.BOTH:
					case BackgroundConstraints.HORIZONTAL:
						return 0;
					default:
						switch (bgc.position) {
						case BackgroundConstraints.WEST:
						case BackgroundConstraints.NORTHWEST:
							return 0;
						case BackgroundConstraints.EAST:
						case BackgroundConstraints.NORTHEAST:
							return componentWidth - imgWidth;
						case BackgroundConstraints.NORTH:
						case BackgroundConstraints.SOUTH:
						case BackgroundConstraints.CENTER:
							return (componentWidth - imgWidth) / 2;
						default:
							return 0;
						}
					}
				}

				@Override
				public int getDistY1() {
					if (bgc.position == BackgroundConstraints.ABSOLUTE) {
						return bgc.y;
					}

					if (bgc.repeat == BackgroundConstraints.VERTICAL || bgc.repeat == BackgroundConstraints.BOTH) {
						return 0;
					}

					switch (bgc.fill) {
					case BackgroundConstraints.BOTH:
					case BackgroundConstraints.VERTICAL:
						return 0;
					default:
						switch (bgc.position) {
						case BackgroundConstraints.NORTH:
						case BackgroundConstraints.NORTHWEST:
						case BackgroundConstraints.NORTHEAST:
							return 0;
						case BackgroundConstraints.SOUTH:
						case BackgroundConstraints.SOUTHEAST:
						case BackgroundConstraints.SOUTHWEST:
							return componentHeight - imgHeight;
						case BackgroundConstraints.EAST:
						case BackgroundConstraints.WEST:
						case BackgroundConstraints.CENTER:
							return (componentHeight - imgHeight) / 2;
						default:
							return 0;
						}
					}
				}

				@Override
				public int getDistX2() {
					if (bgc.position == BackgroundConstraints.ABSOLUTE) {
						return bgc.x + bgc.width;
					}

					if (bgc.repeat == BackgroundConstraints.HORIZONTAL || bgc.repeat == BackgroundConstraints.BOTH) {
						return imgWidth;
					}

					switch (bgc.fill) {
					case BackgroundConstraints.BOTH:
					case BackgroundConstraints.HORIZONTAL:
						return componentWidth;
					default:
						switch (bgc.position) {
						case BackgroundConstraints.WEST:
						case BackgroundConstraints.NORTHWEST:
							return imgWidth;
						case BackgroundConstraints.EAST:
						case BackgroundConstraints.NORTHEAST:
							return componentWidth;
						case BackgroundConstraints.NORTH:
						case BackgroundConstraints.SOUTH:
						case BackgroundConstraints.CENTER:
							return (componentWidth + imgWidth) / 2;
						default:
							return 0;
						}
					}
				}

				@Override
				public int getDistY2() {
					if (bgc.position == BackgroundConstraints.ABSOLUTE) {
						return bgc.y + bgc.height;
					}

					if (bgc.repeat == BackgroundConstraints.VERTICAL || bgc.repeat == BackgroundConstraints.BOTH) {
						return imgHeight;
					}

					switch (bgc.fill) {
					case BackgroundConstraints.BOTH:
					case BackgroundConstraints.VERTICAL:
						return componentHeight;
					default:
						switch (bgc.position) {
						case BackgroundConstraints.NORTH:
						case BackgroundConstraints.NORTHWEST:
						case BackgroundConstraints.NORTHEAST:
							return imgHeight;
						case BackgroundConstraints.SOUTH:
						case BackgroundConstraints.SOUTHEAST:
						case BackgroundConstraints.SOUTHWEST:
							return componentHeight;
						case BackgroundConstraints.EAST:
						case BackgroundConstraints.WEST:
						case BackgroundConstraints.CENTER:
							return (componentHeight + imgHeight) / 2;
						default:
							return 0;
						}
					}
				}

				@Override
				public int getSourceX1() {
					return 0;
				}

				@Override
				public int getSourceY1() {
					return 0;
				}

				@Override
				public int getSourceX2() {
					return this.imgWidth;
				}

				@Override
				public int getSourceY2() {
					return this.imgHeight;
				}

				@Override
				public int getDistStartX() {
					return this.getDistX1();
				}

				@Override
				public int getDistStartY() {
					return this.getDistY1();
				}

				@Override
				public int getDistEndX() {
					if (bgc.repeat == BackgroundConstraints.BOTH || bgc.repeat == BackgroundConstraints.HORIZONTAL) {
						return componentWidth;
					} else {
						return getDistX2();
					}
				}

				public int getDistEndY() {
					if (bgc.repeat == BackgroundConstraints.BOTH || bgc.repeat == BackgroundConstraints.VERTICAL) {
						return componentWidth;
					} else {
						return getDistY2();
					}
				}

			};
		}

	}
}
